// 因为操作数据库最耗时的是连接数据库，所以对数据库进行封装，解决重复连接数据库问题
// 简单封装后存在多个实例化重复调用数据连接的问题，所以在封装时要解决。
const {MongoClient, ObjectId} = require('mongodb'); // 引入数据库
const config_db = require('./config'); // 引入db配置文件

/**
 * 封装db库
 */

class Db {
  // 创建一个静态方法，解决多个实例重复连接数据库的问题
  // 比如实例testDb1已经连接过数据库了，
  // 但是实例testDb2仍然会调用connect方法 去连接数据库，浪费了性能
  // 我们需要的是，当前面有实例连接过数据库时， 
  // 数据库处于连接状态，那么以后的实例都不需要再去连接了
  static getInstance() {
    if(!Db.instance) { // 如果不存在实例
      Db.instance = new Db(); // 就创建实例
    }
    return Db.instance;
  }
  constructor() {
    // 设置一个属性 解决某个实例上多个方法重复调用数据库连接的问题
    // 比如实例testDb已经连接过数据库了，那么在用find查询时，就不要再去重复连接了
    this.dbClient = ''; 

    this.connect(); // 初始化的时候就连接数据库
  }

  connect() { // 连接数据库
    return new Promise((resolve, reject) => {
      if(!this.dbClient) { // 如果dbClient不存在，就说明没调用过
        MongoClient.connect(config_db.dbUrl, (err, client) => {
          if(err) {
            reject(err);
          } else {
            this.dbClient = client.db(config_db.dbName);
            resolve(this.dbClient);
          }
        })
      } else { // 如果已经存在 说明被调用过了
        return resolve(this.dbClient);
      }
    })
  }

  // 添加一个文档
  add(collectionName, jsonData) { 
    return new Promise((resolve, reject) => {
      this.connect()
      .then(db => {
        db.collection(collectionName)
        .insertOne(jsonData, (err, data) => {
          if(err) {
            reject(err);
          } else {
            resolve(data);
          }
        })
      })
      .catch(err => reject(err));
    })
  }

  // 添加多个文档
  addMany(collectionName, jsonArr) { 
    return new Promise((resolve, reject) => {
      this.connect()
      .then(db => {
        db.collection(collectionName)
        .insertMany(jsonArr, (err, data) => {
          if(err) {
            reject(err);
          } else {
            resolve(data);
          }
        })
      })
      .catch(err => reject(err));
    })
  }

  /**删除一条数据 */
  delOne(collectionName, jsonData) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .deleteOne(jsonData, (err, data) => {
              if(err) {
                reject(err);
              }else {
                resolve(data);
              }
            })
        })
    })
  }

  /**删除多条数据 */
  delMany(collectionName, jsonData) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .deleteMany(jsonData, (err, data) => {
              if(err) {
                reject(err);
              }else {
                resolve(data);
              }
            })
        })
    })
  }

  // 更新时删除一条数据
  update_delOne(collectionName, jsonData1, jsonData2) { 
    return new Promise((resolve, reject) => {
      this.connect()
      .then(db => {
        db.collection(collectionName)
        .updateOne(jsonData1, { $unset: jsonData2 }, (err, data) => {
          if(err) {
            reject(err);
          } else {
            resolve(data);
          }
        })
      })
    })
  }

  // 更新单个文档中的数据
  updateValue(collectionName, jsonData1, jsonData2) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(jsonData1, { $set: jsonData2 }, (err, data) => {
              if (err) {
                reject(err)
              } else {
                resolve(data)
              }
            })
        })
      .catch(err => reject(err))
    })
  }

  // 更新多个文档中的数据
  updateManyValue(collectionName, jsonData1, jsonData2) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateMany(jsonData1, { $set: jsonData2 }, (err, data) => {
              if (err) {
                reject(err)
              } else {
                resolve(data)
              }
            })
        })
      .catch(err => reject(err))
    })
  }

  // 更新嵌套文档中指定字段顺序的数据
  updateInSortDeep(collectionName, jsonData1, jsonData2) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .update(jsonData1, {
            $set: jsonData2
            }, (err, data) => {
              if (err) {
                reject(err)
              } else {
                resolve(data)
              }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 嵌套更新增加一条数据
  updateDeep(collectionName, jsonData1, jsonData2) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(jsonData1, { $addToSet: jsonData2 }, (err, data) => {
              if (err) {
                reject(err)
              } else {
                resolve(data)
              }
            })
        })
      .catch(err => reject(err))
    })
  }

  // 删除嵌套数组中的一条数据
  updataDelInArr(collectionName, jsonData1, jsonData2) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(jsonData1, { $pull: jsonData2 }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
      })
    })
  }

  // 删除一条任务
  delOneTask(collectionName, userName, taskId) {

    return new Promise((resolve, reject) => {

      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne({
              userName,
              'allTasks.taskId': taskId
            }, { $pull: { allTasks: { taskId } } }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
      })
    })
  }

  // 删除一条自定义节日
  removeOneFestival(collectionName, userName, createTime) {

    return new Promise((resolve, reject) => {

      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne({
              userName,
              'customizeFestival.createTime': createTime
            }, { $pull: { customizeFestival: { createTime } } }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
      })
    })
  }

  // 清除指定日期所有任务
  clearAllTasks(collectionName, userName, dayId) {

    return new Promise((resolve, reject) => {

      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne({
              userName,
              'allTasks.dayId': dayId
            }, { $pull: { allTasks: { dayId } } }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
      })
    })
  }

  // 设置任务已完成
  setTaskDone(collectionName, userName, taskId) {

    return new Promise((resolve, reject) => {

      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne({
              userName,
              'allTasks.taskId': taskId
            }, { $set: {'allTasks.$.isDone' : true } }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
      })
    })
  }

  // 重新编辑某个自定义节日
  modifyFestival(collectionName, userName, createTime, reData) {

    return new Promise((resolve, reject) => {

      this.connect() 
        .then(db => {
          db.collection(collectionName)
            .updateOne({
              userName,
              'customizeFestival.createTime': createTime,

            }, { $set: { 'customizeFestival.$': reData } }, (err, data) => {
              if (err) {
                reject(err)
              } else {
                resolve(data)
              }
          })
        })
        .catch(err => reject(err));
    })
  }

  // 查找单个/多条数据
  find(collectionName, jsonData) { 
    return new Promise((resolve, reject) => {
      this.connect()
      .then(db => {
        db.collection(collectionName).find(jsonData)
        .toArray((err, data) => {
          if(err) {
            reject(err);
          } else {
            resolve(data);
          }
        })
      })
      .catch(err => reject(err));
    })
  }

  // 排序+限制条数+其他条件查找
  findInSortAndLimit(collectionName, otherCondition, sortCondition, limitNum) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .aggregate([
              otherCondition,
              { $sort: sortCondition},
              { $limit: limitNum }
            ])
            .toArray((err, data) => {
              if(err) {
                reject(err);
              } else {
                resolve(data);
              }
            })
        })
        .catch(err => reject(err));
    })
  }

  // 查找集合内所有文档
  findAll(collectionName) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName).find({})
          .toArray((err, data) => {
              if(err) {
                reject(err);
              } else {
                resolve(data);
              }
          })
        })
        .catch(err => reject(err))
    })
  }

  // 查找嵌套数据
  findInArr(collectionName, key, value) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName).find({
            [key]: {
              $eq: value
            }
          })
          .toArray((err, data) => {
              if(err) {
                reject(err);
              } else {
                resolve(data);
              }
          })
        })
        .catch(err => reject(err))
    })
  }

  // 在allTasks中，按照userName和指定dayId查找
  findDayIdInAllTasks(collectionName, userName, taskDate) {
    
    return new Promise((resolve, reject) => {

      this.connect()
        .then(db => {
          db.collection(collectionName).find({
            userName,
            'allTasks.dayId': {
              $eq: taskDate
            }
          })
          .toArray((err, data) => {
              if(err) {
                reject(err);
              } else {
                resolve(data);
              }
          })
        })
        .catch(err => reject(err))
    })
  }

  // 降序 + 条件查找
  findInSort(collectionName, key, value) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName).find({ [key]: { $gt: value } })
            .sort({ [key]: -1 })
          .toArray((err, data) => {
              if(err) {
                reject(err);
              } else {
                resolve(data);
              }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 按条件查找 多条件
  findInNE(collectionName, jsonData, key, value) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName).find({ ...jsonData, [key]: { $ne: value } }) // 这里是两个查找条件
          .toArray((err, data) => {
            if(err) {
              reject(err);
            } else {
              resolve(data);
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 获取_id，因为查询时用到的_id的值是ObjectId()类型的数据
  getObjectId(id) { 
    return new ObjectId(id);
  }

}

module.exports = Db.getInstance();